using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using mbhxnaengine.Code.GameObject;
using mbhxnaengine.Code.Utilities;
using mbhxnaengine.Code.Game;

namespace mbhxnaengine
{
    // Core game class.  This implemented the Game interface provided by XNA
    // and handles things like the standard game loop.
    //
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        // Main interface with the graphics device.
        //
        GraphicsDeviceManager graphics;

        // For now we just have a single sprite batch for all of our 2D rendering.
        //
        SpriteBatch spriteBatch;

        // A level for testing.
        //
        GameLevel mLevel;

        // The selected Cell.
        //
        GOCell mSelectedCell;

        // Constructor.
        //
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            Content.RootDirectory = "Content";
        }

        // Allows the game to perform any initialization it needs to before starting to run.
        // This is where it can query for any required services and load any non-graphic
        // related content.  Calling base.Initialize will enumerate through any components
        // and Initialize them as well.
        //
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            GameObjectManager.pInstance.Initialize(Content);
            DebugMessageDisplay.pInstance.Initialize(Content, graphics.GraphicsDevice);
            GameObject.mGraphics = graphics.GraphicsDevice;

            // Displays the frame rate using the debug msg display
            FrameRate fps = new FrameRate();
            GameObjectManager.pInstance.Add(fps);

            // Allocate our test level
            mLevel = new GameLevel();

            // The amount of space between cells on the board.
            float spacing = 32.0f;

            // Data used for centering the pieces on screen.
            Vector2 boardSize       = new Vector2(spacing * mLevel.pCellStates.GetLength(1), spacing * mLevel.pCellStates.GetLength(0));
            Vector2 screenCenter    = new Vector2(graphics.GraphicsDevice.Viewport.Width * 0.5f, graphics.GraphicsDevice.Viewport.Height * 0.5f);
            Vector2 start           = new Vector2(screenCenter.X - (boardSize.X * 0.5f), screenCenter.Y - (boardSize.Y * 0.5f));

            // We need to keep an array of all the cells we create to that we can link 
            // them together as well go.
            GOCell[,] cellCache = new GOCell[mLevel.pCellStates.GetLength(0),mLevel.pCellStates.GetLength(1)];

            //bool firstActivateFound = false;

            // Loop through each element in the GameLevel object
            for (int row = 0; row < mLevel.pCellStates.GetLength(0); row++)            
            {
                // Our rows need to be displayed offset from each other.  Every other
                // row gets shifted.
                int offset = row % 2;

                for (int col = 0; col < mLevel.pCellStates.GetLength(1); col++)
                {
                    // Get the cell state as it is defined in the GameLevel object
                    int temp = mLevel.pCellStates[row,col];

                    // If a cell has a state of 0 it means it is not being used.
                    if (temp != 0)
                    {
                        // Create a new cell
                        GOCell newCell = new GOCell();

                        cellCache[row, col] = newCell;

                        // Set up the positions in a standard grid fashion with the extra
                        // cavet of having every other row slightly offset.
                        newCell.pPosition = new Vector3((start.X) + (col * spacing) + (offset * (spacing * 0.5f)), start.Y + (row * spacing), 0.0f);

                        // Update the cell visualization based on the state.
                        newCell.pState = temp;

                        // Add the cell first so that the connecting lines get rendered after.
                        GameObjectManager.pInstance.Add(newCell);

                        if (offset != 0)
                        {
                            if (col > 0)
                            {
                                newCell.pSiblingLeft = cellCache[row, col - 1];

                                if (newCell.pSiblingLeft != null)
                                {
                                    newCell.pSiblingLeft.pSiblingRight = newCell;
                                }
                            }
                            if (row > 0)
                            {
                                newCell.pSiblingAboveLeft = cellCache[row - 1, col];

                                if (newCell.pSiblingAboveLeft != null)
                                {
                                    newCell.pSiblingAboveLeft.pSiblingBelowRight = newCell;
                                }
                            }
                            if (row > 0 && col < mLevel.pCellStates.GetLength(1) - 1)
                            {
                                newCell.pSiblingAboveRight = cellCache[row - 1, col + 1];

                                if (newCell.pSiblingAboveRight != null)
                                {
                                    newCell.pSiblingAboveRight.pSiblingBelowLeft = newCell;
                                }
                            }
                        }
                        else
                        {
                            if (col > 0)
                            {
                                newCell.pSiblingLeft = cellCache[row, col - 1];

                                if (newCell.pSiblingLeft != null)
                                {
                                    newCell.pSiblingLeft.pSiblingRight = newCell;
                                }
                            }
                            if (row > 0 && col > 0 )
                            {
                                newCell.pSiblingAboveLeft = cellCache[row - 1, col - 1];

                                if (newCell.pSiblingAboveLeft != null)
                                {
                                    newCell.pSiblingAboveLeft.pSiblingBelowRight = newCell;
                                }
                            }
                            if (row > 0)
                            {
                                newCell.pSiblingAboveRight = cellCache[row - 1, col];

                                if (newCell.pSiblingAboveRight != null)
                                {
                                    newCell.pSiblingAboveRight.pSiblingBelowLeft = newCell;
                                }
                            }
                        }

                        /*
                        if (!firstActivateFound)
                        {
                            firstActivateFound = true;
                            newCell.DisplaySiblingConnections(true);
                            mSelectedCell = newCell;
                        }
                        */
                    }
                    else
                    {
                        cellCache[row, col] = null;
                    }
                }
            }

            mSelectedCell = cellCache[5, 5];
            mSelectedCell.DisplaySiblingConnections(true);

            GOStone tempStone = new GOStone();
            tempStone.pContainingCell = mSelectedCell;
            GameObjectManager.pInstance.Add(tempStone);

            base.Initialize();
        }


        // LoadContent will be called once per game and is the place to load
        // all of your content.
        //
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        // UnloadContent will be called once per game and is the place to unload
        // all content.
        //
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        bool inputBuffer = false;

        // Allows the game to run logic such as updating the world,
        // checking for collisions, gathering input, and playing audio.
        //
        protected override void Update(GameTime gameTime)
        {
            // We need to clear all the messages at the start of the update.  This makes
            // sure that if the update is called at a different frequency to the renderer
            // we don't get duplicate messages.
            DebugMessageDisplay.pInstance.ClearDynamicMessages();
            
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            // We don't want to have to duplicate the logic of what happens when buttons are pressed
            // across all control schemes.  So we make the check for actions platform specific but 
            // leave the resulting logic platfform agnostic.

            // These keep track of which buttons were pressed.
            bool pressedUp = false;
            bool pressedDown = false;
            bool pressedLeft = false;
            bool pressedRight = false;

#if !XBOX
            // The 360 does not support keyboards.
            //
            KeyboardState keyboardstate = Keyboard.GetState();

            if (keyboardstate.IsKeyDown(Keys.Up))
            {
                pressedUp = true;
            }
            
            if (keyboardstate.IsKeyDown(Keys.Down))
            {
                pressedDown = true;
            }
            
            if (keyboardstate.IsKeyDown(Keys.Left))
            {
                pressedLeft = true;
            }
            
            if (keyboardstate.IsKeyDown(Keys.Right))
            {
                pressedRight = true;
            }
#endif //!XBOX

            // Grab the state of the gamepad.  This is safe on all platforms.
            GamePadState gamepadState = GamePad.GetState(PlayerIndex.One);

            DebugMessageDisplay.pInstance.AddDynamicMessage("I LOVE ELSA FAN!");
            DebugMessageDisplay.pInstance.AddDynamicMessage("Up/Down: " + gamepadState.ThumbSticks.Left.Y);
            DebugMessageDisplay.pInstance.AddDynamicMessage("Left/Right: " + gamepadState.ThumbSticks.Left.X);

            if (gamepadState.ThumbSticks.Left.Y > 0)
            {
                pressedUp = true;
            }

            if (gamepadState.ThumbSticks.Left.Y < 0)
            {
                pressedDown = true;
            }

            if (gamepadState.ThumbSticks.Left.X < 0)
            {
                pressedLeft = true;
            } 
            
            if (gamepadState.ThumbSticks.Left.X > 0)
            {
                pressedRight = true;
            }

            if (pressedUp)
            {
                if (!inputBuffer)
                {
                    if (mSelectedCell.pSiblingAboveLeft != null)
                    {
                        mSelectedCell.DisplaySiblingConnections(false);
                        mSelectedCell = mSelectedCell.pSiblingAboveLeft;
                        mSelectedCell.DisplaySiblingConnections(true);
                    }
                }

                inputBuffer = true;
            }
            else if (pressedDown)
            {
                if (!inputBuffer)
                {
                    if (mSelectedCell.pSiblingBelowLeft != null)
                    {
                        mSelectedCell.DisplaySiblingConnections(false);
                        mSelectedCell = mSelectedCell.pSiblingBelowLeft;
                        mSelectedCell.DisplaySiblingConnections(true);
                    }
                }
                inputBuffer = true;
            }
            else if (pressedLeft)
            {
                if (!inputBuffer)
                {
                    if (mSelectedCell.pSiblingLeft != null)
                    {
                        mSelectedCell.DisplaySiblingConnections(false);
                        mSelectedCell = mSelectedCell.pSiblingLeft;
                        mSelectedCell.DisplaySiblingConnections(true);
                    }
                }
                inputBuffer = true;
            }
            else if (pressedRight)
            {
                if (!inputBuffer)
                {
                    if (mSelectedCell.pSiblingRight != null)
                    {
                        mSelectedCell.DisplaySiblingConnections(false);
                        mSelectedCell = mSelectedCell.pSiblingRight;
                        mSelectedCell.DisplaySiblingConnections(true);
                    }
                }
                inputBuffer = true;
            }
            else
            {
                inputBuffer = false;
            }

            // This call will in turn update every GameObject in the game.
            GameObjectManager.pInstance.Update(gameTime);

            base.Update(gameTime);
        }

        // This is called when the game should draw itself.
        //
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // TODO: Add your drawing code here

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

            GameObjectManager.pInstance.Render(spriteBatch);
            DebugMessageDisplay.pInstance.Render(spriteBatch);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
